package fons.model.query.fun;

import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import fons.model.ontology.Dimension;
import fons.model.ontology.Role;
import fons.model.ontology.Value;
import fons.model.ontology.interfaces.Measurement;

public class Number implements Function {
	private final double value;
	private final Measurement measurement;
	private final Map<Role, Set<Dimension>> dimensionsByRole;
	
	public Number () {
		this(0);
	}
	
	public Number (double value) {
		this(value, null);
	}
	
	public Number (double value, Measurement measurement) {
		this(value, measurement, Maps.<Role, Set<Dimension>>newHashMap());
	}
	
	public Number (double value, Measurement measurement, Map<Role, Set<Dimension>> dimensionsByRoles) {
		this.value = value;
		this.measurement = measurement;
		this.dimensionsByRole = dimensionsByRoles;
	}
	
	
	@Override
	public Function apply(Function other) {
		throw new IllegalArgumentException("Cannot use a constant value as a function with an argument!");
	}

	@Override
	public Function apply() {
		return this;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append(value);
		builder.append(measurement == null ? "" : (" " + measurement.getQualifiedName()));

		if (dimensionsByRole != null) {
			builder.append(' ');
			boolean firstLoop = true;
			for (Entry<Role, Set<Dimension>> entry: dimensionsByRole.entrySet()) {
				if (!firstLoop) builder.append(" und ");
				builder.append("mit ");
				boolean firstTime = true;
				for (Dimension dimension: entry.getValue()) {
					if (!firstTime) builder.append(", ");
					builder.append(dimension.getName());
					firstTime = false;
				}
				builder.append(" als ");
				builder.append(entry.getKey().getName());
				firstLoop = false;
			}
		}
		
		return builder.toString();
	}

	public double getValue() {
		return value;
	}

	public Measurement getMeasurement() {
		return measurement;
	}

	public static Function from(Value value, Collection<? extends Role> roles) {
		Map<Role, Set<Dimension>> dimensionsByRole = Maps.newHashMap();
		for (Role role: roles) {
			Dimension dim = value.hasAnyOf(role.getDefinedDimensions());
			dimensionsByRole.put(role, Sets.newHashSet(dim));
		}
		return new Number(value.getValue(), value.getMeasurement(), dimensionsByRole);
	}

	public Set<Dimension> getDimensionsOf(Role role) {
		if (this.dimensionsByRole == null) return Sets.newHashSet();
		return this.dimensionsByRole.get(role);
	}

	public Map<Role, Set<Dimension>> getDimensionsByRole() {
		return Collections.unmodifiableMap(this.dimensionsByRole);
	}
}
